package org.yourscrum.util;

import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
 * <p>This class helps in finding sorting differences.</p>
 *
 * @author Jettro Coenradie
 */
public class ItemSorter {
    private final static String DEFAULT_DELIMITER = "&";

    private String delimiter = DEFAULT_DELIMITER;

    /**
     * <p>Finds the item that is moved. Different scenario's are possible:</p>
     * <ul>
     * <li>move one item to the beginning</li>
     * <li>move one item to the end</li>
     * <li>move one item up</li>
     * <li>move one item down</li>
     * </ul>
     * <p>The {@see ChangedSortOfItems} is used to store the result. The provided delimited strings must have an equal
     * amount of items. A ChangedSortOfItems object is always returned.</p>
     * <p/>
     * <p>The default delimiter is '&', but a different delimiter can be provided</p>
     *
     * @param ids    Delimited string of item with the new sort
     * @param oldIds Delimited string of items with the old sort
     * @return ChangedSortOfItems object containing the change
     */
    public ChangedSortOfItems determineChangeInStoryOrdering(String ids, String oldIds) {
        validateInput(ids, oldIds);

        if (ids.equals(oldIds)) {
            return ChangedSortOfItems.nothingChanged();
        }

        String[] arrayOfCurrentIds = StringUtils.delimitedListToStringArray(ids, delimiter);
        String[] arrayOfOldIds = StringUtils.delimitedListToStringArray(oldIds, delimiter);

        // A change must be found since the items are not equal
        ChangedSortOfItems change = null;
        int indexFirstChangedItem = findchangeInItems(arrayOfCurrentIds, arrayOfOldIds);
        // kan nooit de laatste zijn die anders is
        String nextOfFirstChangedItem = arrayOfCurrentIds[indexFirstChangedItem + 1];

        if (nextOfFirstChangedItem.equals(arrayOfOldIds[indexFirstChangedItem])) {
            change = ChangedSortOfItems.movedBefore(arrayOfCurrentIds[indexFirstChangedItem],
                    arrayOfOldIds[indexFirstChangedItem]);
        } else {
            int lastitem = arrayOfCurrentIds.length - 1;
            for (int indexMovedBefore = indexFirstChangedItem; indexMovedBefore < arrayOfCurrentIds.length;
                 indexMovedBefore++) {
                String movedItem = arrayOfCurrentIds[indexMovedBefore];
                if (indexMovedBefore == lastitem) {
                    change = ChangedSortOfItems.movedToEnd(movedItem);
                    break;
                } else {
                    String aNextOldItem = arrayOfOldIds[indexMovedBefore + 1];
                    if (!movedItem.equals(aNextOldItem)) {
                        change = ChangedSortOfItems.movedBefore(movedItem, aNextOldItem);
                        break;
                    }
                }
            }
        }
        return change;
    }

    private void validateInput(String ids, String oldIds) {
        Assert.notNull(ids, "String with delimited ids cannot be null");
        Assert.notNull(oldIds, "String with delimited old ids cannot be null");
        Assert.isTrue(!ids.equals(oldIds), "Different sizes of compared arrays are not supported");
    }

    private int findchangeInItems(String[] arrayOfCurrentIds, String[] arrayOfOldIds) {
        for (int i = 0; i < arrayOfCurrentIds.length; i++) {
            String possibleMovedItem = arrayOfCurrentIds[i];
            String originalItem = arrayOfOldIds[i];
            if (!possibleMovedItem.equals(originalItem)) {
                return i;
            }
        }
        throw new NoChangeInSortFoundException();
    }

    private class NoChangeInSortFoundException extends RuntimeException {
        // Exception used within this class
    }

    public void setDelimiter(String delimiter) {
        this.delimiter = delimiter;
    }
}
